package lee.vioson.remotedata.repositories

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import lee.vioson.remotedata.bean.DownloadState
import okhttp3.ResponseBody
import java.io.File

/**
 *Author:viosonlee
 *Date:2023/6/2
 *DESCRIPTION:
 */
class CacheFileRepositoryImpl(private val api: lee.vioson.remotedata.ApiServices) :
    CacheFileRepository,
    BaseRepository() {

    override fun downloadFile(url: String, file: File): Flow<DownloadState> = flow {
        val response = api.downloadFile(url).execute()
        if (response.isSuccessful) {
            val contentLength =
                response.body()?.contentLength() ?: response.headers().get("Content-Length")
                    ?.toLongOrNull() ?: -1
            response.body()?.toFile(file) {
                emit(DownloadState.InProgress(it))
            }
            emit(DownloadState.Success(file,contentLength))
        } else {
            emit(DownloadState.Error(Throwable("The file not found")))
        }

    }.catch {
        emit(DownloadState.Error(it))
    }.flowOn(Dispatchers.IO)


    private inline fun ResponseBody.toFile(file: File, progressChange: (Int) -> Unit): Boolean {
        var bytesCopied = 0
        val total = contentLength()
        var emittedProgress = 0
        file.outputStream().use { output ->
            val input = byteStream()
            val buffer = ByteArray(4096)
            var bytes = input.read(buffer)
            while (bytes >= 0) {
                output.write(buffer, 0, bytes)
                bytesCopied += bytes
                bytes = input.read(buffer)
                val progress = (bytesCopied * 100 / total).toInt()
                if (progress - emittedProgress > 0) {
                    emittedProgress = progress
                    progressChange(progress)
                }
            }
            output.flush()
        }
        return true
    }
}

